package com.sc.nft.dao;

import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.sc.nft.annotation.PartitionField;
import com.sc.nft.annotation.PartitionPK;
import com.sc.nft.annotation.PartitionTable;
import com.sc.nft.entity.ExpenditureRecord;
import com.sc.nft.entity.vo.ExpenditureRecordVO;
import com.sc.nft.entity.vo.MerchantViewBillVO;
import com.sc.nft.enums.ExpenditureRecordTypeEnum;
import com.sc.nft.helper.TableIdGenerator;
import com.sc.nft.helper.TablePartitionExecutor;
import com.sc.nft.helper.TablePartitionRouter;
import com.sc.nft.mapper.ExpenditureRecordMapper;
import com.sc.nft.service.ExpenditureRecordService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import javax.annotation.Resource;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.stream.Collectors;

import static com.sc.nft.constant.TableNameConstants.EXPENDITURE_RECORD;

/**
 * (sc_expenditure_record)数据DAO
 * <p>
 * 非本模块应使用 ExpenditureRecordService 接口，而非直接使用 ExpenditureRecordDao，表 sc_expenditure_record 按 $userId 分表100张
 *
 * @author Sun
 * @description 由 Mybatisplus Code Generator 创建
 * @see ExpenditureRecordService
 * @since 2022-09-07 12:59:34
 */
@Slf4j
@Repository
public class ExpenditureRecordDao {

    @Resource
    private ExpenditureRecordMapper baseMapper;

    @Autowired
    private TableIdGenerator tableIdGenerator;

    private static final String TABLE = EXPENDITURE_RECORD;

    @Transactional
    public Boolean saveBatch(List<ExpenditureRecord> expenditureRecords) {
//        return expenditureRecordMapper.saveBatch(expenditureRecords);

        // 分区插入
        if (CollectionUtils.isEmpty(expenditureRecords)) {
            return true;
        }

        final TablePartitionRouter.Router router = TablePartitionRouter.ofExpenditureRecord();
        final AtomicBoolean fail = new AtomicBoolean(false);
        expenditureRecords
                .stream()
                .filter(item -> item != null && item.getUserId() != null)
                .collect(Collectors.groupingBy(item -> router.routeWithField(item.getUserId())))
                .forEach((partition, items) -> {
                    TablePartitionExecutor.exec(partition, () -> {
                        // 生成并填充ID列表
                        tableIdGenerator.fillAutoIncrementIds(partition, items, this::nextIds, ExpenditureRecord::setId);
                        // 批量插入
                        if (!baseMapper.saveBatch(items)) {
                            fail.set(true);
                        }
                    });
                });

        return !fail.get();
    }

    private List<Long> nextIds(String partition, int count) {
        return tableIdGenerator.defaultNextIds(TABLE, partition, count);
    }

    @PartitionTable(TABLE)
    public Page<ExpenditureRecordVO> getExpenditureRecord(@PartitionField Long userId, String calendar, Integer pageNo, Integer pageSize, Integer type) {
        return baseMapper.getExpenditureRecord(userId, calendar, new Page(pageNo, pageSize), ExpenditureRecordTypeEnum.byTypeGetList(type));
    }

    @PartitionTable(TABLE)
    public Page<MerchantViewBillVO> merchantViewBill(@PartitionField Long userId, Date startTime, Date endTime, Integer pageNo, Integer pageSize, ExpenditureRecordTypeEnum type) {
        return baseMapper.merchantViewBill(userId, startTime, endTime, new Page(pageNo, pageSize), type != null ? type.getCode() : null);
    }

    /**
     * 替代原充血模型中的 #insert() 方法
     */
    @Transactional
    public void insert(ExpenditureRecord model) {
        // 分区实现
        this.saveBatch(Collections.singletonList(model));
    }

    @PartitionTable(TABLE)
    public ExpenditureRecord findById(@PartitionPK Long id) {
        return baseMapper.selectById(id);
    }

}